package cc.cinemate.api;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import cc.cinemate.api.domain.*;
import cc.cinemate.api.domain.common.User;
import cc.cinemate.api.domain.movie.CinemateFilm;
import cc.cinemate.api.response.ResponseMovieNew;
import cc.cinemate.api.response.ResponseMovieSearch;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.format.DateFormat;

public class CinemateAPI {
	
	private CinemateUrlFactory urlFactory = new CinemateUrlFactory("", CinemateResponseFormat.xml);
	private Serializer serializer = new Persister(new DateMatcher());
	
	private String EnumerateArrayInXmlUsingRegex(String xml, String[] arrayTagNames){
		Pattern namePattern = Pattern.compile("<name>");
		for(String arrayTagName : arrayTagNames){		
			Pattern pattern = Pattern.compile(String.format("<%s>(<name>[^</name>]*</name>)+</%s>", arrayTagName, arrayTagName));
			Matcher matcher = pattern.matcher(xml);
			while(matcher.find()){
				Matcher nameMatcher = namePattern.matcher(matcher.group());
				int count = 0;
				while(nameMatcher.find()) {
					count++;
			    }
				String xmlPartWithLength = matcher.group().replace(String.format("<%s>", arrayTagName), String.format("<%s length=\"%d\">", arrayTagName, count));
				xml = xml.replace(matcher.group(), xmlPartWithLength);
			}
		}
		return xml;
	}
	
	private String EnumerateArrayInXml(String xml, String[] arrayTagNames){
		String Splitter = "<name>";
		for(String arrayTagName : arrayTagNames){	
			int positionStart;
			String openTag = String.format("<%s>", arrayTagName);
			String closeTag = String.format("</%s>", arrayTagName);	
			while((positionStart = xml.indexOf(openTag)) > -1){
				int basis = xml.substring(positionStart).indexOf(closeTag) + closeTag.length();
				String xmlPart = xml.substring(positionStart, positionStart + basis);
				int subStringsCount = xmlPart.split(Splitter).length - 1;
				String xmlPartWithLength = xmlPart.replace(openTag, String.format("<%s length=\"%d\">", arrayTagName, subStringsCount));
				xml = xml.replace(xmlPart, xmlPartWithLength);
			}
		}
		return xml;
	}
	
	public List<CinemateFilm> getMovieSearch(String term) {
		ResponseMovieSearch response = new ResponseMovieSearch();
		String xmlResponse = downloadString(urlFactory.MovieSearch(term));
		try {
			serializer.read(response, xmlResponse);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return response.Movies;
	}
	
	public List<CinemateFilm> getMovieNew() {
		long start = System.nanoTime();
		String xmlResponse = downloadString(urlFactory.MovieNew());
		xmlResponse = "{\"name\" : { \"first\" : \"Joe\", \"last\" : \"Sixpack\" }, \"gender\" : \"MALE\", \"userImage\" : \"Rm9vYmFyIQ==\"}";
		ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
		try {
			User user = mapper.readValue(xmlResponse, User.class);
			double downloadedInSec = (System.nanoTime() - start) * 1.0e-9;
		} catch (JsonParseException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (JsonMappingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		
		double downloadedInSec = (System.nanoTime() - start) * 1.0e-9;
		
		xmlResponse = EnumerateArrayInXml(xmlResponse, new String[]{"country", "genre", "director", "cast"});
		double replacedInSec = (System.nanoTime() - start) * 1.0e-9;
		ResponseMovieNew result = new ResponseMovieNew();
		try {
			result = serializer.read(ResponseMovieNew.class, xmlResponse);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		double serializedInSec = (System.nanoTime() - start) * 1.0e-9;
		return result.Movies;
	}
	
	public <T extends Object> T getEntity(T clazz, String url) {
		String xmlResponse = downloadString(urlFactory.MovieNew());
		try {
			return serializer.read(clazz, xmlResponse);
		} catch (Exception e) {
			return null;
		}
    }
	
	public String downloadString(String url) {
		return new String(downloadBytes(url));
	}
	
	public Bitmap downloadBitmap(String url) {
		byte[] bytes = downloadBytes(url);
		return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
	}
	
	public byte[] downloadBytes(String url) {
		byte[] downloadedBytes = null;
		if (url != null) {
			BufferedInputStream bis = null;
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			BufferedOutputStream bos = new BufferedOutputStream(baos);
			try {
				URL urlEntity = new URL(url);
				HttpURLConnection connection = (HttpURLConnection)urlEntity.openConnection();
				connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729)");
				connection.addRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
				connection.addRequestProperty("Accept-Language", "ru,en-us;q=0.7,en;q=0.3");
				connection.addRequestProperty("Accept-Charset", "windows-1251,utf-8;q=0.7,*;q=0.7");
				connection.addRequestProperty("Keep-Alive", "115");
				connection.connect();
				bis = new BufferedInputStream(connection.getInputStream());
				byte[] buffer = new byte[1024 * 8];
				int readedBytesCount;
				while((readedBytesCount = bis.read(buffer, 0, buffer.length)) > 0){
					baos.write(buffer, 0, readedBytesCount);
				}
				baos.flush();
				downloadedBytes = baos.toByteArray();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				if (bis != null) {
					try {
						bis.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if (bos != null) {
					try {
						bos.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if (baos != null) {
					try {
						baos.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		return downloadedBytes;
	}
	
}